package xin.nick.cloud.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author Nick
 * @date 2021/8/1
 * @description 防止爆刷请求
 */
@Slf4j
@Component
@RefreshScope
public class PreventFloodFilter implements GlobalFilter, Ordered {

    /**
     * ip请求的次数map
     */
    private static Map<String, Integer> ipRequestCountMap = new HashMap<>();

    /**
     * ip最后一次请求的时间
     */
    private static Map<String, Long> ipLastRequestTimeMap = new HashMap<>();


    /**
     * 请求时间区间 秒
     */
    @Value("${my.request.duration}")
    private Integer duration;

    /**
     * 请求的数量
     */
    @Value("${my.request.requestCountMax}")
    private Integer requestCountMax;


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        // 当前时间
        Long currentTime = System.currentTimeMillis();

        // 从上下⽂中取出request
        ServerHttpRequest request = exchange.getRequest();

        // 从request对象中获取客户端ip
        String clientIp =
                request.getRemoteAddress().getHostString();

        // 拿到map,获取请求的次数
        Integer requestCount = ipRequestCountMap.get(clientIp);
        if (Objects.isNull(requestCount)) {
            requestCount = 1;
        }
        requestCount++;
        ipRequestCountMap.put(clientIp, requestCount);

        // 拿到上次请求的时间
        Long lastTime = ipLastRequestTimeMap.get(clientIp);
        if(Objects.isNull(lastTime)) {
            lastTime = currentTime;
        }
        // 将这次请求的时间放进去
        ipLastRequestTimeMap.put(clientIp, currentTime);
        Long limitTime = lastTime + duration * 1000L;
        // 如果距离上次请求已经超过了最大的时间间隔,则清理请求次数记录
        if (currentTime.compareTo(limitTime) > 0) {
            requestCount = 0;
            ipRequestCountMap.put(clientIp, requestCount);
        } else {
            // 如果在时间段内,请求次数大于设置值,则拦截下来

            if(requestCount.compareTo(requestCountMax) > 0) {

                ServerHttpResponse response = exchange.getResponse();

                // 状态码
                response.setStatusCode(HttpStatus.SEE_OTHER);
                log.info("=====>IP: {} 请求过多!!!", clientIp);
                String data = "您频繁进⾏请求，请求已被拒绝";
                DataBuffer wrap =
                        response.bufferFactory().wrap(data.getBytes());
                return response.writeWith(Mono.just(wrap));
            }
        }

        // 放行
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
